#include <iostream>
#include <fstream>
#include <map>
#include "pin.H"
#include <string>
ofstream OutFile;

// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 cur_count = 0;
static std::map<string, UINT64> last_write;
static std::map<UINT64, UINT64> rely_distance;
static UINT64 unread_count = 0;

// This function is called before every instruction is executed
VOID RelyDistance() {
	cur_count++;
}
    
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
	// Insert a call to docount before every instruction, no arguments are passed
	INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)RelyDistance, IARG_END);
	UINT64 cur_distance;
	UINT32 read_num, write_num;
	UINT32 i;
	REG cur_reg;
	//cur_count++;
	read_num = INS_MaxNumRRegs(ins);
	write_num = INS_MaxNumWRegs(ins);
	for(i=0;i<write_num;i++)
	{
		cur_reg = INS_RegW(ins, i);
		last_write[REG_StringShort(cur_reg)] = cur_count;
	}

	for(i=0;i<read_num;i++)
	{
		cur_reg = INS_RegR(ins, i);
		if(last_write.find(REG_StringShort(cur_reg)) != last_write.end())
		{
			cur_distance = cur_count - last_write[REG_StringShort(cur_reg)];
			rely_distance[cur_distance]++;
		}
		else
		{
			unread_count++;
			//cout << unread_count << ":read " << REG_StringShort(cur_reg) << " without write" << endl;
			continue;
		}		
	}
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "./lab0_2.out", "specify output file name");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
	map<UINT64, UINT64>::iterator temp;
	double ratio;
    // Write to a file since cout and cerr maybe closed by the application
    OutFile.setf(ios::showbase);
    OutFile << "myCount " << cur_count << endl;
	for(temp = rely_distance.begin();temp != rely_distance.end(); temp++)
	{
		//OutFile << "dis: " << (temp -> first) << "count: " << (temp -> second) << endl;
		OutFile << (temp->first) << "->" ;
		OutFile << (temp->second) << "->" ;
		ratio = (float)100*(temp->second)/(cur_count-unread_count);
		OutFile << fixed << setprecision(5) << ratio << "%" << endl;
	}
    OutFile.close();
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool calculate the rely distance." << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    OutFile.open(KnobOutputFile.Value().c_str());

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);

    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);
    
    // Start the program, never returns
    PIN_StartProgram();
    
    return 0;
}

